/*
前面我们了解了类的加载，同时会提取一个类的信息生成Class对象存放在内存中。而反射机制就是利用这些存放的Class类信息，来获取Class类的信息和操作类。
那么如何获取到每个类对应的Class对象呢？
Class<>是一个泛型，是什么类型的Class,<>里就填写什么类型
 */
package Class_Object;

import java.lang.reflect.Type;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<String> clazz = String.class;  //使用class关键字，通过类名.class获取String的Class对象，Class<>是泛型，是什么类型就填什么类型
        Class<?> clazz2 = Class.forName("java.lang.String");  //使用Class类静态方法forName(),通过括号里的包名.类名获取，注意返回值是Class<?>，?是通配符，不确定什么类型
        Class<?> clazz3 = new String("cpdd").getClass();  //通过类的实例对象获取Class对象

        //判断三者是不是引用同一个对象，类之间和引用类型之间使用==判断。下面三者比较都为true
        System.out.println(clazz == clazz2);
        System.out.println(clazz == clazz3);
        System.out.println(clazz2 == clazz3);
        System.out.println(clazz);
        //通过比较，验证了一开始的结论，在JVM中每个类始终只存在一个Class对象，无论通过什么方式获取，都是一样的。


        //基本数据类型有Class对象么？有的，基本类型也有Class对象
        Class<?> clazz4 = int.class;
        System.out.println(clazz4);  //打印结果为int，基本类型的int，不是integer
        //不是类才有Class对象么，基本数据类型又不是类，也有Class对象忙么？实际上，基本类型也有对应的Class对象（反射操作可能会用到）
        //而且我们不仅可以通过class关键字获取，其实本质上是定义在对应的包装类中的
        /* 查看Integer.java源码,发现有这么一句，定义了一个Class<Integer>类型的常量，叫TYPE，储存的是get int的原始类型即int。为什么写Integer不写int，因为泛型不支持基本类型
        public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
        实际上，每个包装类（包括Void），都有一个获取原始类型的Class方法。注意，getPrimitiveClass()获取的是原始类型，不是包装类型，只是可以用包装类来表示
         */
        //我们可以验证下，Integer.TYPE 和int.class是否相同
        System.out.println(Integer.TYPE == clazz4);  //结果为true，说明两者是同一个东西

        //问题又来了，包装类型的Class对象和基本类型的Class对象是同一个东西么
        System.out.println(Integer.class == clazz4);  //结果为false，idea也在代码中提示了 always false
        //由此可见，包装类型的Class对象和基本类型的Class对象不一样。

        //数组也是一种类型，只是编程不可见(也是C++实现），因此我们可以直接获取数组的的Class对象
        Class<String[]> clazz5 = String[].class;  //万物皆可.class
        System.out.println(clazz5.getName());  //获取类名称（得到的是包名+类名的完整名称） [Ljava.lang.String; [代表是数组，反编译代码里也能见到
        System.out.println(clazz5.getSimpleName());  //获取简易类名称，没有包名 String[] ，泛型里写的什么，就是什么
        System.out.println(clazz5.getTypeName());  //  包名.类名 java.lang.String[]
        System.out.println(clazz5.getClassLoader());  //获取类加载器，String[]的类加载器是BootstrapClassLoader，所以显示为null
//        System.out.println(clazz5.cast(new Integer("10")));  //强制类型转换，cast是转换的意思，Integer转换为String数组会报错，类型转换错误

        //再谈instanceof。instanceof可以判断一个对象是否为某个接口或类的实现，或是子类。
        String str = "aaa";
        System.out.println(str instanceof String); //以前我们判断使用instanceof进行类型比较
        //现在可以通过Class类对象的特性去判断
        System.out.println(str.getClass() == String.class);  //使用getClass()和.class判断，获取的类对象一样，是同一个类型 true
        //判断一个类是否为子类或是接口/抽象类的具体实现，可以使用asSubClass()方法
        Integer i = 10;
        System.out.println(i.getClass().asSubclass(Number.class)); //当Integer不是Number的子类时，会产生异常

        //通过getSuperclass()方法，我们可以获取到父类的Class对象
        System.out.println(i.getClass().getSuperclass());  //class java.lang.Number
        //也可以通过getGenericSuperclass()获取父类的原始类型的Type,Type是一个接口，但不只Class一个实现类，还有其他实现类
        Type type = i.getClass().getGenericSuperclass();
        System.out.println(type);  //class java.lang.Number 发现Type实际上是Class类的父接口，但是获取到的Type的实现不一定是Class
        System.out.println(type instanceof Class);  //true

        //同理，我们可以像上面这样获取父接口
        for (Class<?> anInterface: i.getClass().getInterfaces()) {
            System.out.println(anInterface.getName());  //java.lang.Comparable
        }

        for (Type genericInterface : i.getClass().getGenericInterfaces()){
            System.out.println(genericInterface.getClass());  //class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
            System.out.println(genericInterface.getTypeName());  //java.lang.Comparable<java.lang.Integer>,可以得到泛型，输出出来了
        }
    }
}
